//
// This file is part of aMule Project
//
// Copyright (c) 2004-2006 Angel Vidal (Kry) ( kry@amule.org )
// Copyright (c) 2004-2006 aMule Project ( http://www.amule-project.net )
// Copyright (C)2003 Barry Dunne (http://www.emule-project.net)

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA

// Note To Mods //
/*
Please do not change anything here and release it..
There is going to be a new forum created just for the Kademlia side of the client..
If you feel there is an error or a way to improve something, please
post it in the forum first and let us look at it.. If it is a real improvement,
it will be added to the offical client.. Changing something without knowing
what all it does can cause great harm to the network if released in mass form..
Any mod that changes anything within the Kademlia side will not be allowed to advertise
there client on the eMule forum..
*/

#include "KademliaUDPListener.h"
#include "Prefs.h"
#include "Kademlia.h"
#include "SearchManager.h"
#include "Contact.h"
#include "RoutingZone.h"
#include "ByteIO.h"
#include "OPCodes.h"
#include "Defines.h"
#include "utilities.h"
#include "MemFile.h"

extern "C" {
#include "net.h"
}
#include <zlib.h>
#include <string.h>
#include <iostream>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


////////////////////////////////////////
using namespace std;
////////////////////////////////////////


unsigned long int route_count = 0;

void CKademliaUDPListener::bootstrap(uint32 ip, uint16 port)
{
        lastoutput =  KadClog_get_current_dvn_time();
	KadC_logt(STARTUP_PRINT,"Bootstrap using node %i:%i\n", ip, port);
	
	sendMyDetails(KADEMLIA_BOOTSTRAP_REQ, ip, port);
	
	addHelloCount(ip);

}

void CKademliaUDPListener::sendMyDetails(byte opcode, uint32 ip, uint16 port)
{
	
	unsigned char data[27];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) opcode;

	putint128(&pb, parentInstance->getPrefs()->getKadID() );
	putulong(&pb,parentInstance->getPrefs()->getIPAddress() );
	putushort(&pb,parentInstance->getPrefs()->GetEffectiveUDPPort() );
	putushort(&pb, parentInstance->getPrefs()->GetPort() );
	*pb++= (unsigned char) parentInstance->getPrefs()->GetType();

	parentInstance->sendPacket(data, sizeof(data), ip, port);

	
}

void CKademliaUDPListener::firewalledCheck(uint32 ip, uint16 port)
{

  // [AC] Temporarily removing firewall checks to reduce schduler load
  // and speed up the simulator
  // [dfk] The nodes in the simulations are directly accessible, so removing this this check sounds reasonable
  return;
  /*
	KadC_logt(GENERAL_PRINT,"Firewall check using node %i:%i\n", ip, port);

	//CMemFile bio(2);
	unsigned char data[4];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) KADEMLIA_FIREWALLED_REQ;
	putushort(&pb, parentInstance->getPrefs()->GetPort() );
	parentInstance->sendPacket(data, sizeof(data), ip, port);
  */
}

void CKademliaUDPListener::sendNullPacket(byte opcode,uint32 ip, uint16 port)
{

	KadC_logt(GENERAL_PRINT,"send null packet!\n");
	unsigned char data[2];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) opcode;
	parentInstance->sendPacket(data, sizeof(data), ip, port);
}

void CKademliaUDPListener::processPacket(const byte* data, uint32 lenData, uint32 ip, uint16 port)
{
	
	//comupte stats every interval
	if ( KadClog_get_current_dvn_time() > (lastoutput + out_interval) ) {
		int actual_interval =  KadClog_get_current_dvn_time() - lastoutput;
		unsigned long int bytes_sent = parentInstance->get_bytes_sent();
		int packets_sent = parentInstance->get_packet_sent();
		float pck_recv = (float)packets_recv / (float)actual_interval;
		float pck_sent = (float)packets_sent / (float)actual_interval;
		/* //[dfk]  Might need this to illustrate what's going on.  Need a description of what this is
		KadC_logt(SPECIAL_PRINT, "%.5f, %.5f,", ((float)bytes_recv /(float)actual_interval),pck_recv); 
		KadC_log(SPECIAL_PRINT, " %.5f, %.5f, %d,", ((float)bytes_sent /(float)actual_interval), pck_sent, parentInstance->getRoutingZone()->getNumContacts() );
		KadC_log(SPECIAL_PRINT," %d\n",route_count); 
		*/
		lastoutput =  KadClog_get_current_dvn_time();
 		parentInstance->clear_bytes_sent(); 
		parentInstance->clear_packets_sent(); 
		bytes_recv = 0;
		packets_recv = 0;
	}
	//track stats
	packets_recv++;
	bytes_recv += lenData;

	//Update connection state only when it changes.
	bool curCon = parentInstance->getPrefs()->hasHadContact();
	parentInstance->getPrefs()->setLastContact();
	if( curCon != parentInstance->getPrefs()->hasHadContact()) {
	  //theApp.ShowConnectionState();
	}


	byte opcode = data[1];
	const byte *packetData = data + 2;
	uint32 lenPacket = lenData - 2;
	//KadC_logt(GENERAL_PRINT,"Processing packet of type: %x\n",opcode);

	switch (opcode) {
		case KADEMLIA_BOOTSTRAP_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadBootstrapReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			//printf("processBootstrapRequest\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Bootstrap Request\n", ip);//Completes the string started in net.c
			processBootstrapRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_BOOTSTRAP_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadBootstrapRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//printf("processBootstrapResponse\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Bootstrap Response\n", ip);//Completes the string started in net.c
			processBootstrapResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_HELLO_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadHelloReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			//printf("processHelloRequest\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Hello Request\n", ip);//Completes the string started in net.c
			processHelloRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_HELLO_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadHelloRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//printf("processHelloResponse\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Hello Response\n", ip);//Completes the string started in net.c
			processHelloResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//printf("processKademliaRequest\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Kademlia Request\n", ip);//Completes the string started in net.c
			processKademliaRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //printf("processKademliaResponse\n");
		        //KadC_logt(GENERAL_PRINT,"%i: Kademlia Response", ip);//Completes the string started in net.c
			processKademliaResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_SEARCH_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadSearchReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//processSearchRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_SEARCH_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadSearchRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//processSearchResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_PUBLISH_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadPublishReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//processPublishRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_PUBLISH_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadPublishRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));			
			//processPublishResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_SRC_NOTES_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadSearchNotesReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			processSearchNotesRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_SRC_NOTES_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadSearchNotesRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			processSearchNotesResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_PUB_NOTES_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadPublishNotesReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			processPublishNotesRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_PUB_NOTES_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadPublishNotesRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			processPublishNotesResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_FIREWALLED_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFirewalledReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //KadC_logt(GENERAL_PRINT,"%i: Firewall Request\n", ip);//Completes the string started in net.c
			processFirewalledRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_FIREWALLED_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFirewalledRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //KadC_logt(GENERAL_PRINT,"%i: Firewall Response\n", ip);//Completes the string started in net.c
			processFirewalledResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_FIREWALLED_ACK:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFirewalledAck from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //KadC_logt(GENERAL_PRINT,"%i: Firewall Ack\n", ip);//Completes the string started in net.c
			processFirewalledResponse2(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_FINDBUDDY_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFindBuddyReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //KadC_logt(GENERAL_PRINT,"%i: Find Buddy Request\n", ip);//Completes the string started in net.c
			processFindBuddyRequest(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_FINDBUDDY_RES:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFindBuddyRes from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		        //KadC_logt(GENERAL_PRINT,"%i: Find Buddy Response\n", ip);//Completes the string started in net.c
			processFindBuddyResponse(packetData, lenPacket, ip, port);
			break;
		case KADEMLIA_CALLBACK_REQ:
			//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadCallbackReq from %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
			//processCallbackRequest(packetData, lenPacket, ip, port);
			break;
		default: {
			
		 	KadC_logt(ERROR_PRINT,"%i: Packet type not regular kademlia, discarding\n", ip);
		
		}
	}

}

void CKademliaUDPListener::addContact( const byte *data, uint32 lenData, uint32 ip, uint16 port, uint16 tport)
{
	CMemFile bio((byte*)data, lenData);
	CUInt128 id = bio.ReadUInt128();
	bio.ReadUInt32();
	bio.ReadUInt16();
	if( tport ) {
		bio.ReadUInt16();
	} else {
		tport = bio.ReadUInt16();
	}
	byte type = bio.ReadUInt8();
	//AddDebugLogLineM(false, logKadMain, wxT("Adding a contact with ip ") + Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip),port));
	// Look for existing client
	CContact *contact = parentInstance->getRoutingZone()->getContact(id);
	if (contact != NULL) {
		contact->setIPAddress(ip);
		contact->setUDPPort(port);
		contact->setTCPPort(tport);
	} else {
		//if(IsGoodIPPort(wxUINT32_SWAP_ALWAYS(ip),port)) {
	        // Ignore stated ip and port, use the address the packet came from
		parentInstance->getRoutingZone()->add(id, ip, port, tport, type);
		//}
	}
}

void CKademliaUDPListener::addContacts( const byte *data, uint32 lenData, uint16 numContacts)
{
	CMemFile bio((byte*)data, lenData );
	CRoutingZone *routingZone = parentInstance->getRoutingZone();
	for (uint16 i=0; i<numContacts; i++) {
		CUInt128 id = bio.ReadUInt128();
		uint32 ip = bio.ReadUInt32();
		uint16 port = bio.ReadUInt16();
		uint16 tport = bio.ReadUInt16();
		byte type = bio.ReadUInt8();
		//AddDebugLogLineM(false, logKadMain, wxT("Adding contact(s) with ip ") + Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip),port));
		//if (IsGoodIPPort(wxUINT32_SWAP_ALWAYS(ip),port)) {
		KadC_logt(VERBOSE_PRINT, "Adding ip: %s, port: %i, tport: %i type: %i hash:\n",parentInstance->h_to_a(ip),port,tport,type);
		id.printme(VERBOSE_PRINT);
		KadC_log(VERBOSE_PRINT, "\n");
		routingZone->add(id, ip, port, tport, type);
		//}
	}
}

//KADEMLIA_BOOTSTRAP_REQ
void CKademliaUDPListener::processBootstrapRequest (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket != 25){
			KadC_logt(ERROR_PRINT, "Received wrong size bootstrap request packet!\n");
			return;

	}
	// Add the sender to the list of contacts
	addContact(packetData, lenPacket, ip, port);

	// Get some contacts to return
	ContactList contacts;
	uint16 numContacts = 1 + (uint16)parentInstance->getRoutingZone()->getBootstrapContacts(&contacts, 20);

	// Create response packet
	//We only collect a max of 20 contacts here.. Max size is 527.
	//2 + 25(20) + 15(1)
	//CMemFile bio(527);

	unsigned char data[535];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) KADEMLIA_BOOTSTRAP_RES;
	// Write packet info
	putushort(&pb, numContacts);
	CContact *contact;
	ContactList::const_iterator it;
	for (it = contacts.begin(); it != contacts.end(); it++) {
		contact = *it;
		putint128(&pb, contact->getClientID());
		putulong(&pb, contact->getIPAddress());
		putushort(&pb, contact->getUDPPort());
		putushort(&pb, contact->getTCPPort());
		*pb++ = (unsigned char) contact->getType();
	}
	putint128(&pb, parentInstance->getPrefs()->getKadID() );
	putulong(&pb,parentInstance->getPrefs()->getIPAddress() );
	putushort(&pb,parentInstance->getPrefs()->GetEffectiveUDPPort() );
	putushort(&pb, parentInstance->getPrefs()->GetPort() );
	*pb++ = 0;
	//*pb++= (unsigned char) CKademlia::getPrefs()->GetType();
	uint32 datasize = 1 + 1 + 2 + 25*numContacts;
	parentInstance->sendPacket(data, datasize, ip, port);
	KadC_logt(GENERAL_PRINT,"bootstrap request ok.\n");
	// Send response
	//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadBootstrapRes %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));

	//sendPacket(&bio, KADEMLIA_BOOTSTRAP_RES, ip, port);
}

//KADEMLIA_BOOTSTRAP_RES
void CKademliaUDPListener::processBootstrapResponse (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{

	// Verify packet is expected size
	if (lenPacket < 27) {
		KadC_logt(ERROR_PRINT,"Received wrong size packed for bootstrap resp\n");
	}

 	// How many contacts were given
	CMemFile bio((byte*)packetData, lenPacket);
	uint16 numContacts = bio.ReadUInt16();

		

	// Verify packet is expected size
	if (lenPacket != (uint32)(2 + 25*numContacts)) {
		return;
	}

	// Add these contacts to the list.
	addContacts(packetData+2, lenPacket-2, numContacts);
	// Send sender to alive.
	parentInstance->getRoutingZone()->setAlive(ip, port);
	
	KadC_logt(STARTUP_PRINT,"Bootstrapped ok. Added %i total contacts\n",numContacts);

}

//KADEMLIA_HELLO_REQ
void CKademliaUDPListener::processHelloRequest (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	 addHelloCount(ip);
		
	// Verify packet is expected size
	if (lenPacket != 25){
		KadC_logt(ERROR_PRINT,"received wrong size packet for hello request\n");
		//throw wxString::Format(wxT("***NOTE: Received wrong size (%u) packet in "), lenPacket) + wxString::FromAscii(__FUNCTION__);
	}

	// Add the sender to the list of contacts
	addContact(packetData, lenPacket, ip, port);

	// Send response
	//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadHelloRes %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));	
	sendMyDetails(KADEMLIA_HELLO_RES, ip, port);

	// Check if firewalled
	if(parentInstance->getPrefs()->getRecheckIP()) {
		firewalledCheck(ip, port);
	}
}

//KADEMLIA_HELLO_RES
void CKademliaUDPListener::processHelloResponse (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket != 25){
		KadC_logt(GENERAL_PRINT,"received wrong size packet for hello response\n");
		//throw wxString::Format(wxT("***NOTE: Received wrong size (%u) packet in "), lenPacket) + wxString::FromAscii(__FUNCTION__);
	}

	// Add or Update contact.
	addContact(packetData, lenPacket, ip, port);

	// Set contact to alive.
	parentInstance->getRoutingZone()->setAlive(ip, port);
}

//KADEMLIA_REQ
void CKademliaUDPListener::processKademliaRequest (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{	
        //KadC_logt(GENERAL_PRINT,"kademlia request found, processing\n");
	
	// Verify packet is expected size
	if (lenPacket != 33){
		KadC_logt(ERROR_PRINT,"received wrong size packet for kademlia request\n");
	}

	//RecheckIP and firewall status
	if(parentInstance->getPrefs()->getRecheckIP())
	{
		firewalledCheck(ip, port);
		//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadHelloReq %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		sendMyDetails(KADEMLIA_HELLO_REQ, ip, port);
	}

	// Get target and type
	CMemFile bio((byte*)packetData, lenPacket);
	byte type = bio.ReadUInt8();
//		bool flag1 = (type >> 6); //Reserved
//		bool flag2 = (type >> 7); //Reserved
//		bool flag3 = (type >> 8); //Reserved

	type = type & 0x1F;
	if( type == 0 ) {
		KadC_logt(ERROR_PRINT, "received wrong type: %x\n",type);
	}

	//This is the target node trying to be found.
	CUInt128 target = bio.ReadUInt128();
	CUInt128 distance(parentInstance->getPrefs()->getKadID());
	distance.XOR(target);

	//This makes sure we are not mistaken identify. Some client may have fresh installed and have a new hash.
	CUInt128 check = bio.ReadUInt128();
	if( parentInstance->getPrefs()->getKadID().compareTo(check)) {
		KadC_logt(
			  ERROR_PRINT,
			  "[KADREQ, %s] IP: %s Case of mistaken identity, he thought we were: ",
			  parentInstance->getPrefs()->getIpString().c_str(),
			  parentInstance->h_to_a(ip));
		check.printme(ERROR_PRINT);
		KadC_log(ERROR_PRINT, "\n");
		return;
	}

	// Get required number closest to target
	ContactMap results;
	parentInstance->getRoutingZone()->getClosestTo(2, target, distance, (int)type, &results);
	uint16 count = (uint16)results.size();

	// Write response
	// Max count is 32. size 817.. 
	// 16 + 1 + 25(32)
	unsigned char data[821];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) KADEMLIA_RES;
	//calculate the actual size of the outgoing packet
	
	//CMemFile bio2( 817 );
	putint128(&pb, target);
	*pb++ = (unsigned char) count;
	//bio2.WriteUInt128(target);
	//bio2.WriteUInt8((byte)count);
	CContact *c;
	ContactMap::const_iterator it;
	for (it = results.begin(); it != results.end(); it++) {
		c = it->second;
		putint128(&pb, c->getClientID());
		putulong(&pb, c->getIPAddress());
		putushort(&pb, c->getUDPPort());
		putushort(&pb, c->getTCPPort());
		*pb++ = (unsigned char) c->getType();

		/*
		bio2.WriteUInt128(c->getClientID());
		bio2.WriteUInt32(c->getIPAddress());
		bio2.WriteUInt16(c->getUDPPort());
		bio2.WriteUInt16(c->getTCPPort());
		bio2.WriteUInt8(c->getType());
		*/
	}
	uint32 datasize =1 + 1 + 16 + 1 + count*25; 
	//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadRes %s Count=%u")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port) % count);
	parentInstance->sendPacket(data, datasize, ip, port);
	//sendPacket(&bio2, KADEMLIA_RES, ip, port);
	//KadC_logt(GENERAL_PRINT,"kad response includes %d nodes\n",count);
	
}

//KADEMLIA_RES
void CKademliaUDPListener::processKademliaResponse (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket < 17){
		KadC_log(GENERAL_PRINT,"\n");
		KadC_logt(ERROR_PRINT,"received too small packet for kademlia response\n");
		//throw wxString::Format(wxT("***NOTE: Received wrong size (%u) packet in "), lenPacket) + wxString::FromAscii(__FUNCTION__);	
	}

	//Used Pointers
	CRoutingZone *routingZone = parentInstance->getRoutingZone();

	if(parentInstance->getPrefs()->getRecheckIP()) {
		KadC_log(GENERAL_PRINT,"\n");
		firewalledCheck(ip, port);
		//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadHelloReq %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));
		sendMyDetails(KADEMLIA_HELLO_REQ, ip, port);
	}

	// What search does this relate to
	CMemFile bio((byte*)packetData, lenPacket);
	CUInt128 target = bio.ReadUInt128();
	uint16 numContacts = bio.ReadUInt8();

	// Verify packet is expected size
	if (lenPacket != (uint32)(16+1 + (16+4+2+2+1)*numContacts)) {
		KadC_log(GENERAL_PRINT,"\n");
		KadC_logt(ERROR_PRINT,"received wrong size packet for kademlia response, discarding\n");
		return;
	}

	// Set contact to alive.
	routingZone->setAlive(ip, port);

	
	ContactList *results = new ContactList;
	try {
		for (uint16 i=0; i<numContacts; i++) {
			CUInt128 id = bio.ReadUInt128();
			uint32 contactIP = bio.ReadUInt32();
			uint16 contactPort = bio.ReadUInt16();
			uint16 tport = bio.ReadUInt16();
			byte type = bio.ReadUInt8();
			//the byte swap was removed, this a problem?
			//if(::IsGoodIPPort(contactIP,contactPort)) {
			routingZone->add(id, contactIP, contactPort, tport, type);
			results->push_back(new CContact(id, contactIP, contactPort, tport, target));
			//}
		}
	} catch(...) {
		delete results;
		throw;
	}
	
	
	parentInstance->mySearchMan->processResponse(target, ip, port, results);
	KadC_log(VERBOSE_PRINT," added %i nodes\n", numContacts);//Completes the string started in net.c
	
}

//KADEMLIA_FIREWALLED_REQ
void CKademliaUDPListener::processFirewalledRequest (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket != 2){
		KadC_logt(ERROR_PRINT,"received wrong size packet for firewall request!\n");
	}

	CMemFile bio((byte*)packetData, lenPacket);
	uint16 tcpport = bio.ReadUInt16();

	CContact contact;
	contact.setIPAddress(ip);
	contact.setTCPPort(tcpport);
	contact.setUDPPort(port);
	//theApp.clientlist->RequestTCP(&contact);

	// Send response
	unsigned char data[6];
	//CMemFile bio2(4);
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) KADEMLIA_FIREWALLED_RES;
	putulong(&pb, ip);
	//bio2.WriteUInt32(ip);
	//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFirewalledRes %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));

	parentInstance->sendPacket(data, sizeof(data), ip, port);
	//sendPacket(&bio2, KADEMLIA_FIREWALLED_RES, ip, port);
}

//KADEMLIA_FIREWALLED_RES
void CKademliaUDPListener::processFirewalledResponse (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket != 4){
		KadC_logt(ERROR_PRINT,"received wrong size packet for firewall response!\n");
	}

	// Set contact to alive.
	parentInstance->getRoutingZone()->setAlive(ip, port);

	CMemFile bio((byte*)packetData, lenPacket);
	uint32 firewalledIP = bio.ReadUInt32();

	//Update con state only if something changes.
	if( parentInstance->getPrefs()->getIPAddress() != firewalledIP ) {
		parentInstance->getPrefs()->setIPAddress(firewalledIP);
		//theApp.ShowConnectionState();
	}
	parentInstance->getPrefs()->incRecheckIP();
}

//KADEMLIA_FIREWALLED_ACK
void CKademliaUDPListener::processFirewalledResponse2 (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	// Verify packet is expected size
	if (lenPacket != 0 ) {
		KadC_logt(ERROR_PRINT,"recieved wong size packet for firewall resp2\n");
		//throw wxString::Format(wxT("***NOTE: Received wrong size (%u) packet in "), lenPacket) + wxString::FromAscii(__FUNCTION__);
	}

	// Set contact to alive.
	parentInstance->getRoutingZone()->setAlive(ip, port);
	parentInstance->getPrefs()->incFirewalled();
}

//KADEMLIA_FINDBUDDY_REQ
void CKademliaUDPListener::processFindBuddyRequest (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{
	
	KadC_logt(GENERAL_PRINT,"processing a find buddy request\n");
	
	// Verify packet is expected size
	if (lenPacket < 34) {
		KadC_logt(ERROR_PRINT,"recieved wrong size packet for find buddy request\n");
	}

	if( parentInstance->getPrefs()->getFirewalled() ) {
		//We are firewalled but somehow we still got this packet.. Don't send a response..
		return;
	}

	CMemFile bio((byte*)packetData, lenPacket);
	CUInt128 BuddyID = bio.ReadUInt128();
	CUInt128 userID = bio.ReadUInt128();
	uint16 tcpport = bio.ReadUInt16();

	CContact contact;
	contact.setIPAddress(ip);
	contact.setTCPPort(tcpport);
	contact.setUDPPort(port);
	contact.setClientID(userID);
	//theApp.clientlist->IncomingBuddy(&contact, &BuddyID);

	//CMemFile bio2(34);
	//bio2.WriteUInt128(BuddyID);
	//bio2.WriteUInt128(CKademlia::getPrefs()->getClientHash());
	//bio2.WriteUInt16(CKademlia::getPrefs()->GetPort());

	CUInt128 CHash;
	CHash.setValue(parentInstance->getPrefs()->getClientHash());
	unsigned char data[36];
	unsigned char *pb = data;
	*pb++ = (unsigned char) OP_KADEMLIAHEADER;
	*pb++ = (unsigned char) KADEMLIA_FINDBUDDY_RES;
	putint128(&pb, BuddyID);
	putint128(&pb, CHash);
	putushort(&pb, parentInstance->getPrefs()->GetPort());

	parentInstance->sendPacket(data, sizeof(data), ip, port);	

//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("KadFindBuddyRes %s")) % Uint32_16toStringIP_Port(wxUINT32_SWAP_ALWAYS(ip), port));

	//sendPacket(&bio2, KADEMLIA_FINDBUDDY_RES, ip, port);
}

//KADEMLIA_FINDBUDDY_RES
void CKademliaUDPListener::processFindBuddyResponse (const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port)
{

		KadC_logt(GENERAL_PRINT,"processing find buddy response\n");

	// Verify packet is expected size
	if (lenPacket < 34) {
		KadC_logt(ERROR_PRINT,"wrong size for find buddy response\n");
	}


	CMemFile bio((byte*)packetData, lenPacket);
	CUInt128 check = bio.ReadUInt128();
	check.XOR(CUInt128(true));
	if( parentInstance->getPrefs()->getKadID().compareTo(check)) {
		return;
	}
	CUInt128 userID = bio.ReadUInt128();
	uint16 tcpport = bio.ReadUInt16();

	CContact contact;
	contact.setIPAddress(ip);
	contact.setTCPPort(tcpport);
	contact.setUDPPort(port);
	contact.setClientID(userID);
	//theApp.clientlist->RequestBuddy(&contact);
}

bool CKademliaUDPListener::getPacket(UDPIO *myUDP) {
	unsigned char *buf;
	udpmsg *pum;
	//unsigned long int remoteip;
	//int remoteport;
	//int nrecv;
	unsigned char decompr[4096];
	unsigned short int zstatus;
	unsigned long int unpackedsize;
	int x;
	if(myUDP == NULL)
		return false;

	pum = (udpmsg *) myUDP->udp_recv_fifo->deqtw(myUDP->udp_recv_fifo, FIFOTIMEOUT);
 	if(pum == NULL) 
		return false;
 
	//nrecv = pum->nrecv;
	//remoteip = pum->remoteip;
	//remoteport = pum->remoteport;
	buf = pum->buf;
	if(buf[0] == OP_KADEMLIAPACKEDPROT) {	/* 0xE5: compressed packet, decompress */
		unpackedsize = sizeof(decompr);
		zstatus = uncompress(decompr + 2, &unpackedsize, pum->buf + 2, pum->nrecv - 2);
		if(zstatus != Z_OK) {
			
			KadC_logt(ERROR_PRINT,"uncompress failed!\n");
			return true;	/* drop packet */
		} 
		else {
			KadC_logt(GENERAL_PRINT,"uncompress successful!\n");
		}
		pum->nrecv = unpackedsize+2;
		decompr[0] = OP_KADEMLIAHEADER;
		decompr[1] = buf[1];			/* copy opcode */
		buf = decompr;		/* proceeds with decompressed buffer */
 	}
	else if(buf[0] == OP_KADEMLIAHEADER) {
		KadC_logt(VERBOSE_PRINT,"incoming packet=");
		for(x=0; x<pum->nrecv; x++) {
			KadC_log(VERBOSE_PRINT,"%X:",(int)buf[x]);
		}
		KadC_log(VERBOSE_PRINT, "\n");
	}
	else {
		KadC_logt(ERROR_PRINT,"incorrect header on packet, discarding=");
		for(x=0; x<pum->nrecv; x++) {
			KadC_log(VERBOSE_PRINT,"%X:",(int)buf[x]);
		}
		KadC_log(VERBOSE_PRINT, "\n");
		//we send true because we want to check if there is another packet
		return true;
	}

		
	processPacket((byte *) buf, pum->nrecv, pum->remoteip, pum->remoteport);	
	free(pum->buf);
	free(pum);

	//return true because we got a packet this turn, and want to check for another.
	return true;
}


unsigned long int CKademliaUDPListener::PackPacket(unsigned char *packet, int size)
{
	//wxASSERT(!m_bSplitted);
	unsigned char *temp = packet;
	uLongf newsize = size + 300;
	byte* output = new byte[newsize];

	//Header is attached, we don't want to pack that, so don't send to compress
	uint16 result = compress2(output, &newsize, (byte*) packet+2, size-2, Z_BEST_COMPRESSION);

	if (result != Z_OK) {
		KadC_logt(ERROR_PRINT,"got an error from zlib compress\n");
		delete[] output;
		return 0;
	}

	if (size <= newsize) {
		KadC_logt(ERROR_PRINT,"error packing, size not reduced\n");
		delete[] output;
		return 0;
	}

	if (*temp == (unsigned char) OP_KADEMLIAHEADER) {
		*temp = (unsigned char) OP_KADEMLIAPACKEDPROT;
	} else {
		*temp = (unsigned char) OP_PACKEDPROT;
	}
	
	memcpy(packet+2, output, newsize);
	delete[] output;
		
	return newsize;
}

unsigned short int CKademliaUDPListener::getushort(unsigned char **ppb) {
	unsigned short u;
	u = *(*ppb)++;
	u += (*(*ppb)++)<<8 ;
	return u;
}
unsigned long int CKademliaUDPListener::getulong(unsigned char **ppb) {
	unsigned long u;
	u = *(*ppb)++;
	u += (*(*ppb)++)<<8;
	u += (*(*ppb)++)<<16;
	u += (*(*ppb)++)<<24;
	return u;
}
/*
int128 getint128(int128 hash, unsigned char **ppb) {
	int i, j;
	for(i=0; i<16; i+=4)
		for(j=3; j>=0; j--)
			hash[i+j] = *(*ppb)++;
	return hash;
}
*/
unsigned char *CKademliaUDPListener::putushort(unsigned char **ppb, unsigned short int u) {
	*(*ppb)++ = (unsigned char)u; u = u>>8;
	*(*ppb)++ = (unsigned char)u;
	return *ppb;
}

unsigned char *CKademliaUDPListener::putulong(unsigned char **ppb, unsigned long int u) {
	*(*ppb)++ = (unsigned char)u; u = u>>8;
	*(*ppb)++ = (unsigned char)u; u = u>>8;
	*(*ppb)++ = (unsigned char)u; u = u>>8;
	*(*ppb)++ = (unsigned char)u;
	return *ppb;
}

unsigned char *CKademliaUDPListener::putint128(unsigned char **ppb, const CUInt128& hash) {
	for (int i = 0; i < 4; i++) {
		putulong(ppb, hash.get32BitChunk(i));
	}
	return *ppb;
}



//Function(s) to keep track of unique HELLO requests, to aid in tracking how many peoples
//routing tables we are on. 


bool CKademliaUDPListener::openIPFile() {

  return true;
  /*

	srand(time(NULL));
	char IPfilename[] = "iplist.txt";
	

	IPlogfile = fopen(IPfilename, "a+");
	
	if(IPlogfile == NULL) {
		KadC_logt(ERROR_PRINT, "Error creating / opening IP list, exiting\n");
		return false;
	}

	return true;
  */
}


void CKademliaUDPListener::addHelloCount(uint32 IP) {
	
  return;
  /*

	if(IPlogfile == NULL) {
		KadC_logt(ERROR_PRINT, "Error accessing IPlogfile!\n");
		return;
	}

	fflush(stdout);
	//char buf[64];
	//memset(buf,0,64);
	uint32 tempIP = 0;
	rewind( IPlogfile );
	
	while ( !feof(IPlogfile) ) {
		fscanf(IPlogfile,"%lu", &tempIP);
		if(tempIP == IP ) {
			return;
		}
	}
	fprintf(IPlogfile, "%lu\n", IP);
	route_count++;		

  */
}

void
CKademliaUDPListener::sendSearchNotesReq(
  const CUInt128 &target, uint32 ip, uint16 port) {

  byte data[18];
  byte *pb = data;
  *pb = static_cast<byte>(OP_KADEMLIAHEADER);
  pb++;
  *pb = static_cast<byte>(KADEMLIA_SRC_NOTES_REQ);
  pb++;
  putint128(&pb, target);
  parentInstance->sendPacket(data, sizeof(data), ip, port);
}

void
CKademliaUDPListener::processSearchNotesRequest(
  const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port) {

	CMemFile bio(const_cast<byte *>(packetData), lenPacket);
	CUInt128 target = bio.ReadUInt128();

  // Print some logging info
  struct sockaddr_in n_addr;
  char *a_addr = NULL;
  n_addr.sin_addr.s_addr = htonl(ip);
  a_addr = inet_ntoa(n_addr.sin_addr);
  KadC_logt(
    BOT_PRINT,
    "[NOTEREQ, %s] from %s, hash: %s, found: ",
    parentInstance->getPrefs()->getIpString().c_str(),
    a_addr != NULL ? a_addr : "n/a",
    target.toString().c_str());

  byte *data = NULL;
  try {
    SimpleTagList tagList = parentInstance->myNoteIndex.getNote(target);
    size_t dataSize = SimpleTag::encodedTagListSize(tagList);
    dataSize += (1 +1 + 16);
    data = new byte[dataSize];
    byte *pb = data;
    *pb = static_cast<byte>(OP_KADEMLIAHEADER);
    pb++;
    *pb = static_cast<byte>(KADEMLIA_SRC_NOTES_RES);
    pb++;
    putint128(&pb, target);
    SimpleTag::encodeTagList(tagList, &pb);
    parentInstance->sendPacket(data, dataSize, ip, port);
    delete[] data;

    KadC_log(BOT_PRINT, "yes\n");
  } catch (...) {
    delete[] data;

    KadC_log(BOT_PRINT, "no\n");

    // If there is not a corresponding note, then an exception will be
    // thrown.  The original code seems to ignore the case where no
    // entry is found to the point of not responding.  Sounds simple
    // enough to me.
  }
}

void
CKademliaUDPListener::processSearchNotesResponse(
  const byte *packetData, uint32 lenPacket, uint32 ip, uint16 port) {

  CMemFile bio(const_cast<byte *>(packetData), lenPacket);
  CUInt128 target = bio.ReadUInt128();
  byte *packet = const_cast<byte *>(packetData) + bio.GetPosition();
  SimpleTagList tagList = SimpleTag::decodeTagList(&packet);
  parentInstance->mySearchMan->processResult(target, tagList);
}

void
CKademliaUDPListener::sendPublishNotesReq(
  const CUInt128 &target, SimpleTagList &tagList, uint32 ip, uint16 port) {

  byte *data = NULL;
  size_t dataSize = SimpleTag::encodedTagListSize(tagList);
  dataSize += (1 + 1 + 16);
  data = new byte[dataSize];
  byte *pb = data;
  *pb = static_cast<byte>(OP_KADEMLIAHEADER);
  pb++;
  *pb = static_cast<byte>(KADEMLIA_PUB_NOTES_REQ);
  pb++;
  putint128(&pb, target);
  SimpleTag::encodeTagList(tagList, &pb);
  parentInstance->sendPacket(data, dataSize, ip, port);
  delete[] data;
}

void
CKademliaUDPListener::processPublishNotesRequest(
  const byte* packetData, uint32 lenPacket, uint32 ip, uint16 port) {

  CMemFile bio(const_cast<byte *>(packetData), lenPacket);
  CUInt128 target = bio.ReadUInt128();
  byte *packet = const_cast<byte *>(packetData) + bio.GetPosition();
  SimpleTagList tagList = SimpleTag::decodeTagList(&packet);
  parentInstance->myNoteIndex.addNotes(target, tagList);

  // Print out some logging info
  struct sockaddr_in n_addr;
  char *a_addr = NULL;
  n_addr.sin_addr.s_addr = htonl(ip);
  a_addr = inet_ntoa(n_addr.sin_addr);
  std::string fileName("");
  uint32 fileSize = 0;
  SimpleTagList::iterator i = tagList.begin();
  for (; i != tagList.end(); i++) {
    switch (i->getType()) {
      case SimpleTag::STRING_TYPE:
        fileName = i->getStringValue();
        break;
      case SimpleTag::UINT32_TYPE:
        fileSize = i->getUint32Value();
        break;
    }
  }
  KadC_logt(
    BOT_PRINT,
    "[NOTESTORE, %s] from: %s, hash: %s, filename: %s, filesize: %d\n",
    parentInstance->getPrefs()->getIpString().c_str(),
    a_addr != NULL ? a_addr : "n/a",
    target.toString().c_str(),
    fileName.c_str(),
    fileSize);


  byte data[1 + 1 + 16];
  byte *pb = data;
  *pb = static_cast<byte>(OP_KADEMLIAHEADER);
  pb++;
  *pb = static_cast<byte>(KADEMLIA_PUB_NOTES_RES);
  pb++;
  putint128(&pb, target);
  parentInstance->sendPacket(data, sizeof(data), ip, port);
}

void
CKademliaUDPListener::processPublishNotesResponse(
  const byte* packetData, uint32 lenPacket, uint32 ip, uint16 port) {

  CMemFile bio(const_cast<byte *>(packetData), lenPacket);
  CUInt128 target = bio.ReadUInt128();
  parentInstance->mySearchMan->processPublishResult(target, 0, false);
}
